﻿using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using Sgr.Tests;
using Sgr.Utilities;
using Sgr.Admin.WebHost;
using Sgr.Data;
using Sgr.Indentity.Application.ViewModels;
using Xunit.Sgr.Indentity.Helpers;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Sgr.Oss;
using Sgr.Oss.Services;
using Microsoft.AspNetCore.Http;

namespace Xunit.Sgr.Tests
{
    /// <summary>
    /// SGR管理后台Web应用程序工厂类
    /// 用于集成测试的Web应用程序初始化和配置
    /// </summary>
    public class SgrAdminWebApplicationFactory : SgrWebApplicationFactory<Program>
    {
        #region 常量定义

        private const string TOKEN_URL = "api/v1/sgr/oauth/token";
        private const string BEARER_SCHEME = "Bearer";

        #endregion 常量定义

        #region 公共方法

        /// <summary>
        /// 创建具有管理员权限的HTTP客户端
        /// </summary>
        /// <param name="withToken">是否包含访问令牌</param>
        /// <returns>HTTP客户端实例</returns>
        public virtual HttpClient CreateAdminHttpClient(bool withToken = true)
        {
            return CreateNewClient(withToken, isAuthorized: true);
        }

        /// <summary>
        /// 创建未授权的HTTP客户端
        /// </summary>
        /// <param name="withToken">是否包含访问令牌</param>
        /// <returns>HTTP客户端实例</returns>
        public virtual HttpClient CreateUnauthorizedClient(bool withToken = true)
        {
            return CreateNewClient(withToken, isAuthorized: false);
        }

        /// <summary>
        /// 在指定范围内执行异步操作
        /// </summary>
        /// <param name="action">要执行的异步操作</param>
        public async Task ExecuteScopeAsync(Func<IServiceProvider, Task> action)
        {
            using var scope = Services.CreateScope();
            await action(scope.ServiceProvider);
        }

        /// <summary>
        /// 在指定范围内执行带返回值的异步操作
        /// </summary>
        /// <typeparam name="T">返回值类型</typeparam>
        /// <param name="action">要执行的异步操作</param>
        /// <returns>操作结果</returns>
        public async Task<T> ExecuteScopeAsync<T>(Func<IServiceProvider, Task<T>> action)
        {
            using var scope = Services.CreateScope();
            return await action(scope.ServiceProvider);
        }

        #endregion 公共方法

        #region 受保护的方法

        /// <summary>
        /// 创建新的HTTP客户端
        /// </summary>
        /// <param name="withToken">是否包含访问令牌</param>
        /// <param name="isAuthorized">是否授权用户</param>
        /// <returns>HTTP客户端实例</returns>
        /// <exception cref="HttpRequestException">获取访问令牌失败时抛出</exception>
        protected virtual HttpClient CreateNewClient(bool withToken = true, bool isAuthorized = true)
        {
            var client = CreateClient();

            if (withToken)
            {
                string authToken = AsyncHelper.RunSync(() => GetAccessTokenAsync(isAuthorized));

                if (string.IsNullOrEmpty(authToken))
                {
                    throw new HttpRequestException("获取访问令牌失败");
                }

                client.DefaultRequestHeaders.Authorization =
                    new AuthenticationHeaderValue(BEARER_SCHEME, authToken);
            }

            return client;
        }

        /// <summary>
        /// 配置Web主机
        /// </summary>
        /// <param name="builder">Web主机构建器</param>
        protected override void ConfigureWebHost(IWebHostBuilder builder)
        {
            builder.ConfigureServices(services =>
            {
                services.AddMediatR(cfg =>
                {
                    cfg.RegisterServicesFromAssemblyContaining<SgrIntegrationTestFixture>();
                });

                // 移除原有数据库连接配置，使用测试数据库
                services.RemoveAll<IDataConnectionStringProvider>();
                services.TryAddEnumerable(ServiceDescriptor.Singleton<IDataConnectionStringProvider, TestDataConnectionStringProvider>());

                if (typeof(LocalFilesystemOssService).Equals(
                    services.FirstOrDefault(f => f.ServiceType == typeof(IOssService))?.ImplementationType))
                {
                    services.PostConfigure<LocalFilesystemOptions>(options =>
                    {
                        options.BaseUrl = "http://localhost";
                    });
                }
            });
        }

        /// <summary>
        /// 准备主机环境
        /// </summary>
        /// <param name="host">主机实例</param>
        protected override void PrepareHost(IHost host)
        {
            var serviceProvider = host.Services;
            AsyncHelper.RunSync(() => SeedDataHelper.InitData(serviceProvider));
        }

        /// <summary>
        /// 清理主机环境
        /// </summary>
        /// <param name="serviceProvider">服务提供者</param>
        protected override async Task CleanupHost(IServiceProvider serviceProvider)
        {
            await SeedDataHelper.ClearDataAsync();
        }

        /// <summary>
        /// 配置主机环境
        /// </summary>
        /// <param name="builder"></param>
        protected override void ConfigureHostEnvironment(IHostBuilder builder)
        {
            Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Development");//Production Development
            Environment.SetEnvironmentVariable("SGR_PERMIT_LIMIT", "100000");
        }

        #endregion 受保护的方法

        #region 私有方法

        /// <summary>
        /// 获取访问令牌
        /// </summary>
        /// <param name="isAdmin">是否为管理员令牌</param>
        /// <returns>访问令牌字符串</returns>
        private async Task<string> GetAccessTokenAsync(bool isAdmin = false)
        {
            using var client = CreateClient();
            var command = isAdmin
                ? AuthenticationTestHelper.CreateAdminLoginCommand()
                : AuthenticationTestHelper.CreateCustomLoginCommand();

            var response = await client.PostAsJsonAsync(TOKEN_URL, command);
            var result = await response.Content.ReadFromJsonAsync<TokenViewModel>();

            return result?.AccessToken ?? string.Empty;
        }

        #endregion 私有方法
    }
}